package se.callista.microservices.composite.product.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import se.callista.microservices.composite.product.model.ProductAggregated;
import se.callista.microservices.model.Product;
import se.callista.microservices.model.Recommendation;
import se.callista.microservices.model.Review;
import se.callista.microservices.util.ServiceUtils;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import static java.util.concurrent.CompletableFuture.supplyAsync;
import static java.util.concurrent.CompletableFuture.allOf;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.bouncycastle.asn1.x509.X509ObjectIdentifiers.id;
/**
* Created by magnus on 04/03/15.
*/
@Produces(APPLICATION_JSON)
@Consumes(APPLICATION_JSON)
@RestController
public class ProductCompositeService {
private static final Logger LOG = LoggerFactory.getLogger(ProductCompositeService.class);
@Autowired
ProductCompositeIntegration integration;
@Autowired
ServiceUtils util;
@RequestMapping("/")
public String getProduct() {
return "{\"timestamp\":\"" + new Date() + "\",\"content\":\"Hello from ProductAPi\"}";
}
@RequestMapping("/{productId}")
public ResponseEntity<ProductAggregated> getProduct(@PathVariable int productId) {
// 1. First get mandatory product information
Product product = getBasicProductInfo(productId);
// 2. Get optional recommendations
List<Recommendation> recommendations = getRecommendations(productId);
// 3. Get optional reviews
List<Review> reviews = getReviews(productId);
return util.createOkResponse(new ProductAggregated(product, recommendations, reviews, util.getServiceAddress()));
}
// @RequestMapping("/{productId}")
public ResponseEntity<ProductAggregated> getProductAsync(@PathVariable int productId) {
try {
CompletableFuture<Product> productFuture = supplyAsync( () -> getBasicProductInfo(productId));
CompletableFuture<List<Recommendation>> recommendationListFuture = supplyAsync( () -> getRecommendations(productId));
CompletableFuture<List<Review>> reviewListFuture = supplyAsync( () -> getReviews(productId));
LOG.info("Asynch, allOf.join...");
allOf(productFuture, recommendationListFuture, reviewListFuture).join();
LOG.info("Asynch, create result and return...");
return util.createOkResponse(new ProductAggregated(productFuture.get(), recommendationListFuture.get(), reviewListFuture.get(), util.getServiceAddress()));
} catch (InterruptedException | ExecutionException e) {
LOG.error("getProductAsync error", e);
throw new RuntimeException(e);
}
}
private Product getBasicProductInfo(@PathVariable int productId) {
ResponseEntity<Product> productResult = integration.getProduct(productId);
Product product = null;
if (!productResult.getStatusCode().is2xxSuccessful()) {
// Something went wrong with getProduct, simply skip the product-information in the response
LOG.debug("Call to getProduct failed: {}", productResult.getStatusCode());
} else {
product = productResult.getBody();
}
return product;
}
private List<Review> getReviews(@PathVariable int productId) {
ResponseEntity<List<Review>> reviewsResult = integration.getReviews(productId);
List<Review> reviews = null;
if (!reviewsResult.getStatusCode().is2xxSuccessful()) {
// Something went wrong with getReviews, simply skip the review-information in the response
LOG.debug("Call to getReviews failed: {}", reviewsResult.getStatusCode());
} else {
reviews = reviewsResult.getBody();
}
return reviews;
}
private List<Recommendation> getRecommendations(@PathVariable int productId) {
List<Recommendation> recommendations = null;
try {
ResponseEntity<List<Recommendation>> recommendationResult = integration.getRecommendations(productId);
if (!recommendationResult.getStatusCode().is2xxSuccessful()) {
// Something went wrong with getRecommendations, simply skip the recommendation-information in the response
LOG.debug("Call to getRecommendations failed: {}", recommendationResult.getStatusCode());
} else {
recommendations = recommendationResult.getBody();
}
} catch (Throwable t) {
LOG.error("getProduct error", t);
throw t;
}
return recommendations;
}
}